package com.slipper.system.menu.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import com.slipper.exception.CommonException;
import com.slipper.util.Tool;
import com.slipper.util.UUID;
import com.slipper.system.menu.bo.MenuPrivilegeBO;
import com.slipper.system.menu.dao.MenuDao;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import com.slipper.constants.Constants;
import com.slipper.system.menu.bo.MenuBO;
import com.slipper.core.transaction.ReadTransactional;
import com.slipper.core.transaction.WriteTransactional;

import java.util.*;

@Service
@ReadTransactional
public class MenuService {
    @Resource
    private MenuDao menuDao;
    @Resource
    private MenuPrivilegeService menuPrivilegeService;

    public MenuBO queryBy(String id) {
        MenuBO menuBO = menuDao.queryBy(id);
        MenuPrivilegeBO menuPrivilegeBO = new MenuPrivilegeBO();
        menuPrivilegeBO.setMenuId(id);
        menuBO.setPrivilegeList(menuPrivilegeService.queryList(menuPrivilegeBO));
        return menuBO;
    }
    
    public PageInfo<MenuBO> queryPage(MenuBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(menuDao.queryList(bo));
    }
    
    @WriteTransactional
    public Integer save(MenuBO bo) {
        if (bo.getParentId() == null) {
            bo.setParentId(Constants.ROOT_ID);
        }

        if (bo.getParentId().equals(bo.getId())) {
            throw new CommonException("不能设置上级菜单为当前菜单！");
        }

        int result = 0;
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            result += menuDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            result += menuDao.insert(bo);
        }

        result += menuPrivilegeService.save(bo.getId(), bo.getPrivilegeList());
        return result;
    }

    @WriteTransactional
    public void saveList(List<MenuBO> menuList) {
        if (menuList == null) return;

        menuList.forEach(menu -> {
            if (menuDao.queryBy(menu.getId()) != null) {
                menu.setUpdateTime(System.currentTimeMillis());
                menuDao.update(menu);
            } else {
                menu.setUpdateTime(System.currentTimeMillis());
                menu.setCreateTime(System.currentTimeMillis());
                menuDao.insert(menu);
            }
            menuPrivilegeService.saveList(menu.getPrivilegeList());
        });
    }
    
    @WriteTransactional
    public Integer delete(MenuBO bo) {
        checkUse(bo.getId());
        checkUse(bo.getIds());
        return menuDao.delete(bo);
    }

    private void checkUse(String...ids) {
        if (ids == null) return;

        for (String id : ids) {
            if (Tool.isBlank(id)) continue;
            if (menuDao.queryUseById(id) > 0) throw new CommonException("菜单已被使用，不允许删除！");
        }
    }
    
    public List<MenuBO> queryUserMenus(MenuBO bo) {
        String employeeId = bo.getEmployeeId();
        if (Constants.getCurrentUser().isSuperAdmin()) {
            MenuBO menuBO = new MenuBO();
            menuBO.setSubsystem(bo.getSubsystem());
            menuBO.setSubsystemId(bo.getSubsystemId());
            menuBO.setValid(Constants.VALID);
            List<MenuBO> menuList = this.menuDao.queryList(menuBO);
            this.fillPrivilege(menuList, this.menuPrivilegeService.queryList(new MenuPrivilegeBO()));
            return menuList;
        } else {
            MenuBO menuBO = new MenuBO();
            menuBO.setSubsystem(bo.getSubsystem());
            menuBO.setSubsystemId(bo.getSubsystemId());
            menuBO.setEmployeeId(employeeId);
            menuBO.setValid(Constants.VALID);
            List<MenuBO> menuList = this.menuDao.queryList(menuBO);
            
            // 加载父菜单
            Set<String> parentMenuIds = new HashSet<>();
            for (MenuBO menu : menuList) {
                if (parentMenuIds.add(menu.getParentId())) {
                    String parentId = menu.getParentId();
                    while(parentId != null && !Constants.ROOT_ID.equals(parentId)) {
                        MenuBO parentMenu = this.menuDao.queryBy(menu.getParentId());
                        if (parentMenu.getParentId() != null) {
                            if (!parentMenuIds.add(parentMenu.getParentId())) {
                                break;
                            }
                        }
                        parentId = parentMenu.getParentId();
                    }
                }
            }
            if (!parentMenuIds.isEmpty()) {
                MenuBO qo = new MenuBO();
                qo.setIds(parentMenuIds.toArray(new String[] {}));
                List<MenuBO> parentMenuList = this.menuDao.queryList(qo);
                for (MenuBO menu : parentMenuList) {
                    if (Constants.VALID.equals(menu.getValid()) && !menuList.contains(menu)) {
                        menuList.add(menu);
                    }
                }
            }
            return menuList;
        }
    }

    public void fillPrivilege(List<MenuBO> menuList, List<MenuPrivilegeBO> privilegeList) {
        for (MenuBO menu : menuList) {
            privilegeList.stream()
                    .filter(privilege -> menu.getId().equals(privilege.getMenuId()))
                    .forEach(menu::addPrivilege);
        }
    }

    public List<MenuBO> queryList(MenuBO bo) {
        return this.menuDao.queryList(bo);
    }

    @WriteTransactional
    public int updateParentId(MenuBO bo) {
        return menuDao.updateParentId(bo);
    }

    public boolean isParent(String id, String parentId) {
        if (Tool.isBlank(parentId) || Constants.ROOT_ID.equals(parentId)) {
            return false;
        }

        if (Tool.isBlank(id) || Constants.ROOT_ID.equals(id)) {
            return false;
        }

        MenuBO bo = menuDao.queryBy(id);
        if (bo == null) {
            return false;
        } else if (bo.getParentId().equals(parentId)) {
            return true;
        }

        return isParent(bo.getParentId(), parentId);
    }
}
